使用 ruamel.yaml 时如何防止重新格式化 yaml 文件?

How can I prevent re-formating of a yaml file when ruamel.yaml is used?

我在 How to auto-dump modified values in nested dictionaries using ruamel.yaml 的相关答案中使用解决方案 .

我观察到当 self.update(self.yaml.load(f) or {}) 被调用时:它会根据字母顺序对 yaml 文件进行排序,并将其缩进更改为 2,从而不断重新格式化 yaml 文件。

=> 是否可以防止重新格式化?

我也试过下面一行,为了保持缩进为 4 space:

yaml = YAML(typ="safe")
yaml.indent(mapping=2, sequence=4, offset=2)
yaml.default_flow_style = False

config.yaml:

c:  # my comment
    b:  
      f: 5  
      e: 22  
a:
    z: 4
    b: 4  # my comment

代码(与 How to auto-dump modified values in nested dictionaries using ruamel.yaml 完全相同的代码 ):

#!/usr/bin/env python3

import sys
import os
from pathlib import Path
import ruamel.yaml


class SubConfig(dict):
    def __init__(self, parent):
        self.parent = parent

    def updated(self):
        self.parent.updated()

    def __setitem__(self, key, value):
        if isinstance(value, dict):
            v = SubConfig(self)
            v.update(value)
            value = v
        super().__setitem__(key, value)
        self.updated()

    def __getitem__(self, key):
        try:
            res = super().__getitem__(key)
        except KeyError:
            super().__setitem__(key, SubConfig(self))
            self.updated()
            return super().__getitem__(key)
        return res

    def __delitem__(self, key):
        res = super().__delitem__(key)
        self.updated()

    def update(self, *args, **kw):
        for arg in args:
            for k, v in arg.items():
                self[k] = v
        for k, v in kw.items():
            self[k] = v
        self.updated()
        return


_SR = ruamel.yaml.representer.SafeRepresenter
_SR.add_representer(SubConfig, _SR.represent_dict)


class Config(dict):
    def __init__(self, filename, auto_dump=True):
        self.filename = filename if hasattr(filename, "open") else Path(filename)
        self.auto_dump = auto_dump
        self.changed = False
        self.yaml = ruamel.yaml.YAML(typ="safe")
        self.yaml.default_flow_style = False
        if self.filename.exists():
            with open(filename) as f:
                self.update(self.yaml.load(f) or {})

    def updated(self):
        if self.auto_dump:
            self.dump(force=True)
        else:
            self.changed = True

    def dump(self, force=False):
        if not self.changed and not force:
            return
        with open(self.filename, "w") as f:
            self.yaml.dump(dict(self), f)
        self.changed = False

    def __setitem__(self, key, value):
        if isinstance(value, dict):
            v = SubConfig(self)
            v.update(value)
            value = v
        super().__setitem__(key, value)
        self.updated()

    def __getitem__(self, key):
        try:
            res = super().__getitem__(key)
        except KeyError:
            super().__setitem__(key, SubConfig(self))
            self.updated()
        return super().__getitem__(key)

    def __delitem__(self, key):
        res = super().__delitem__(key)
        self.updated()

    def update(self, *args, **kw):
        for arg in args:
            for k, v in arg.items():
                self[k] = v
        for k, v in kw.items():
            self[k] = v
        self.updated()


cfg = Config(Path("config.yaml"))

=> config.yaml 文件更新如下,其中,其排序和缩进变为 2,并删除注释:

a:
  b: 4
  z: 4
c:
  b:
    e: 5
    f: 22

无法阻止缩进“重新格式化”。 ruamel.yaml 记录为 规范化格式,以便所有(块样式)映射缩进相同, 并且所有(块样式)序列都缩进相同。但是,您可以设置其中的每一个,您似乎没有为映射做这些 (YAML 中 Python dicts 的表示):

import sys
import ruamel.yaml

data = dict(a=dict(b=[42, 18]))

yaml = ruamel.yaml.YAML()
yaml.indent(mapping=4, sequence=4, offset=2)
yaml.dump(data, sys.stdout)

这为映射和序列提供了四个缩进:

a:
    b:
      - 42
      - 18

确保使用默认值(往返)loader/dumper 以防止排序并让您完全控制缩进输出。